<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>es5函数对象的原型</title>
  <!-- 
  在JavaScript中，ES5（ECMAScript 5）中的函数对象是一种特殊的对象，除了可以被调用执行外，还具有其他一些特性。函数对象的原型是理解JavaScript中函数的行为和特性的关键。
  一、函数对象的定义
  - 函数对象：在JavaScript中，函数是一等公民，可以像其他对象一样被创建、赋值、传递和返回。每个函数都是一个对象，并且具有 `prototype` 属性。

  二、函数的原型
  1.  `prototype` 属性：
    - 每个函数都有一个 `prototype` 属性，指向一个对象，该对象用于存放实例共享的属性和方法。
    - 当使用构造函数创建对象时，新对象的 `__proto__` 属性（或 `[[Prototype]]` ）指向构造函数的 `prototype` 属性。
  function Person(name) {
      this.name = name;
  }

  Person.prototype.greet = function() {
      console.log('Hello, ' + this.name);
  };

  const alice = new Person('Alice');
  alice.greet(); // 输出: Hello, Alice
  三、函数的特性
  1. 可调用性：
    - 函数对象可以被调用，执行其内部的代码。

  2. 具有属性：
    - 函数对象可以有自己的属性，除了 `prototype` 属性外，还可以添加其他属性。
  function greet() {
    console.log('Hello!');
  }
  
  greet.language = 'English'; // 为函数对象添加属性
  console.log(greet.language); // 输出: English

  3. 构造函数：
    - 函数可以用作构造函数，通过 `new` 关键字创建实例。
  function Car(model) {
      this.model = model;
  }

  Car.prototype.drive = function() {
      console.log('Driving a ' + this.model);
  };

  const myCar = new Car('Toyota');
  myCar.drive(); // 输出: Driving a Toyota

  四、函数的原型链
  1. 原型链继承：
    - 当调用一个函数的属性或方法时，JavaScript会首先查找函数对象自身的属性，如果没有找到，则会沿着原型链查找。
  function Animal(name) {
      this.name = name;
  }

  Animal.prototype.speak = function() {
      console.log(this.name + ' makes a noise.');
  };

  function Dog(name) {
      Animal.call(this, name); // 继承属性
  }

  Dog.prototype = Object.create(Animal.prototype); // 继承方法
  Dog.prototype.constructor = Dog; // 修正构造函数指向

  Dog.prototype.speak = function() {
      console.log(this.name + ' barks.');
  };

  const dog = new Dog('Rex');
  dog.speak(); // 输出: Rex barks.

  五、总结
  - ES5中的函数对象是特殊的对象，具有 `prototype` 属性，用于定义共享的属性和方法。
  - 函数对象不仅可以被调用，还可以有自己的属性，并且可以用作构造函数。
  - 通过原型链，函数对象可以实现继承，允许创建更复杂的对象层次结构。
   -->
</head>
<body>
  
  <script>

    // 1. 函数对象
    // 函数对象是特殊的对象，具有 `prototype` 属性，用于定义共享的属性和方法。
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }

    Person.prototype.sayHello = function() {
      console.log('Hello, my name is ' + this.name);
    };

    const person1 = new Person('Alice', 25);
    const person2 = new Person('Bob', 30);

    person1.sayHello(); // 输出: Hello, my name is Alice

    // 2. 函数对象的属性
    // 函数对象不仅可以被调用，还可以有自己的属性，并且可以用作构造函数。
    function Animal(name) {
      this.name = name;
    }

    Animal.prototype.speak = function() {
      console.log(this.name + ' makes a noise.');
    };

    const dog = new Animal('Rex');
    dog.speak(); // 输出: Rex makes a noise.

    // 3. 原型链
    // 通过原型链，函数对象可以实现继承，允许创建更复杂的对象层次结构。
    function Dog(name) {
      Animal.call(this, name);
    }

    Dog.prototype = Object.create(Animal.prototype); // 继承方法
    Dog.prototype.constructor = Dog; // 修正构造函数指向

    const dog2 = new Dog('Buddy');
    dog2.speak(); // 输出: Buddy makes a noise.

    // 4. 原型链的作用
    // 原型链的作用是共享属性和方法，避免重复创建相同的属性和方法，提高性能和代码的可维护性。
    var obj = {}
    function foo() {}
    // 5.1.将函数看成是一个普通的对象时, 它是具备__proto__(隐式原型)
    // 作用: 查找key对应的value时, 会找到原型身上
    console.log(obj.__proto__) // Object
    console.log(foo.__proto__) // ƒ () { [native code] }

    // 5.2.将函数看成是一个函数时, 它是具备prototype(显式原型)
    // 作用: 用来构建对象时, 给对象设置隐式原型的
    console.log(foo.prototype)
    console.log(foo.prototype.constructor) // ƒ foo() {}
    console.log(foo.prototype.constructor.name) // foo
    // console.log(obj.prototype) 对象是没有prototype
    
  </script>
  
</body>
</html>